﻿@using DeepCloner.Core
@using Masa.Blazor.Components.TemplateTable
@using Masa.Blazor.Components.TemplateTable.Actions
@using Masa.Blazor.Components.TemplateTable.ColumnDialogs
@using Masa.Blazor.Components.TemplateTable.FilterDialogs
@using Masa.Blazor.Components.TemplateTable.Viewers
@using Microsoft.JSInterop
@using Masa.Blazor.Components.TemplateTable.Toolbars
@using Masa.Blazor.Components.TemplateTable.SortDialogs
@using Masa.Blazor.Components.TemplateTable.DetailDialogs
@using Masa.Blazor.Components.TemplateTable.Paginations
@namespace Masa.Blazor
@inject IJSRuntime JSRuntime
@inject IPopupService PopupService

@if (_sheet is not null)
{
    <div class="m-template-table">
        <Toolbar DefaultViewId="@_sheet.DefaultViewId"
                 ShowToolbar="@ShowToolbar"
                 ShowToolbarActions="@ShowToolbarActions"
                 ShowToolbarViews="@ShowToolbarViews"
                 Role="@Role"
                 ActiveView="@_sheet.ActiveViewId"
                 ActiveViewChanged="@HandleOnActiveViewChanged"
                 ColumnOrder="@_sheet.ActiveView.Order"
                 ColumnOrderChanged="@HandleOnColumnOrderChanged"
                 Editable="@Editable"
                 RowHeight="_rowHeight"
                 RowHeightChanged="@HandleOnRowHeightChanged"
                 PageSizeOptions="@_sheet.Pagination.PageSizeOptions"
                 PageSizeOptionsChanged="@HandleOnPageSizeOptionsChanged"
                 @bind-ShowBulkDelete="@_sheet.ActiveView.Value.ShowBulkDelete"
                 Views="_sheet.Views"
                 Columns="_allColumns"
                 HiddenColumnIds="@_sheet.ActiveViewHiddenColumnIds"
                 ViewActionsContent="@ViewActionsContent?.Invoke(_viewActionsContext)"
                 OnViewAdd="@AddNewView"
                 OnViewReset="@ResetView"
                 OnViewRename="@RenameView"
                 OnViewDelete="@DeleteView"
                 OnViewSave="@SaveView"
                 OnColumnEditClick="@OpenColumnEditDialog"
                 OnColumnToggle="@ToggleColumn"
                 HasSelectedKeys="@(_viewActionsContext.HasSelectedRows)"
                 HasActions="@_sheet.ActiveView.Value.ShowActions"
                 HasSelect="@_sheet.ActiveView.Value.ShowSelect"
                 HasCustom="@HasCustomConfiguration"
                 HasFilter="@(_sheet.ActiveView.Value.Filter.Options.Count > 0)"
                 HasSort="@(_sheet.ActiveView.Value.Sort?.Options.Count > 0)"
                 OnFilterClick="@(() => _filterDialog?.Open())"
                 OnSortClick="@(() => _sortDialog?.Open())"
                 OnRowRemove="@Remove"
                 OnSearch="@Search"
                 OnSave="@SaveSheet" />

        <Viewer ViewColumns="@_sheet.ActiveView.Columns"
                HiddenColumnIds="@_sheet.ActiveViewHiddenColumnIds"
                ColumnOrder="@_sheet.ActiveView.Order"
                ColumnOrderChanged="@HandleOnColumnOrderChanged"
                Editable="@Editable"
                Class="@TableClass"
                Loading="@_loading"
                TableStripeClass="@StripeClass"
                TableBodyTdClass="@BodyTdClass"
                TableBodyTrClass="@BodyTrClass"
                TableHeaderThClass="@HeaderThClass"
                TableHeaderClass="@HeaderClass"
                RowHeight="@_rowHeight"
                Rows="@_rows"
                HeaderTextAlign="@HeaderTextAlign"
                Sort="@_sheet.ActiveView.Value.Sort"
                Filter="@_sheet.ActiveView.Value.Filter"
                OnSortUpdate="@SortUpdate"
                HasActions="@_sheet.ActiveView.Value.ShowActions"
                ShowSelect="@_sheet.ActiveView.Value.ShowSelect"
                Height="@Height"
                OnColumnToggle="@ToggleColumn"
                OnColumnEditClick="@OpenColumnEditDialog"
                OnColumnResize="@ResizeColumn"
                OnImagePreview="@OpenImageViewer"
                OnDetail="@OpenDetailDialog"
                CheckboxColor="@CheckboxColor"
                SelectedKeys="@_viewActionsContext.SelectedKeys"
                OnSelect="@HandleOnSelect"
                OnSelectAll="@HandleOnSelectAll"
                HasCustomRowActions="@HasCustomRowActions"
                RowActionsContent="@RowActionsContent"
                CustomCellContent="@CustomCellContent"
                @ref="_viewer">
        </Viewer>

        @if (PaginationContent != null)
        {
            var context = new PaginationContext(_sheet.ActiveView.PageIndex, _sheet.ActiveView.PageSize, _totalCount, _sheet.Pagination.PageSizeOptions, HandleOnPaginationUpdateAsync);
            @PaginationContent(context)
        }
        else
        {
            var pageStart = (_sheet.ActiveView.PageIndex - 1) * _sheet.ActiveView.PageSize;
            var pageStop = Math.Min(_sheet.ActiveView.PageIndex * _sheet.ActiveView.PageSize, _totalCount);

            <Pagination PageIndex="@_sheet.ActiveView.PageIndex"
                        PageStart="@pageStart"
                        PageStop="@pageStop"
                        ItemsPerPage="@_sheet.ActiveView.PageSize"
                        ItemsLength="@_totalCount"
                        PageSizeOptions="@_sheet.Pagination.PageSizeOptions"
                        OnPaginationUpdate="@HandleOnPaginationUpdateAsync">
            </Pagination>
        }

        <ColumnDialog @ref="_columnEditDialog"
                      OnSave="@UpdateColumn">
        </ColumnDialog>

        <FilterDialog @ref="_filterDialog"
                      Show=ShowFilter
                      ShowChanged="ShowFilterChanged"
                      ActiveView="@_sheet.ActiveView.Value"
                      Columns="@_sheet.Columns"
                      Filter=" _sheet!.ActiveView.Value.Filter"
                      FilterChanged="SaveFilter">
        </FilterDialog>

        <SortDialog @ref="_sortDialog"
                    Show=ShowSort
                    ShowChanged="ShowSortChanged"
                    ActiveView="@_sheet.ActiveView.Value"
                    Columns="@_sheet.Columns"
                    HiddenColumnIds="@_sheet.ActiveViewHiddenColumnIds"
                    OnSave="@SortUpdate">
        </SortDialog>

        <ImageViewer @ref="_imageViewer" />

        <DetailDialog @ref="_detailDialog" OnImagePreview="@OpenImageViewer" />

    </div>
}

@code {

    private RowHeight _rowHeight;
    private Viewer? _viewer;

    private ICollection<string>? _defaultColumnIds;

    private ColumnDialog? _columnEditDialog;
    private FilterDialog? _filterDialog;
    private SortDialog? _sortDialog;
    private DetailDialog? _detailDialog;
    private ImageViewer? _imageViewer;

    private bool HasCustomRowActions => RowActionsContent is not null;

    private bool HasCustomConfiguration
    {
        get
        {
            if (_sheet?.ActiveView is null)
            {
                return false;
            }

            ICollection<string> columnIds = [];
            foreach (var column in _sheet.ActiveView.Columns)
            {
                if (column.Hidden)
                {
                    return true;
                }

                columnIds.Add(column.ColumnId);
            }

            _defaultColumnIds ??= _sheet.Columns.Select(u => u.Id).ToList();
            _defaultColumnIds.Remove(Preset.ActionsColumnId);
            columnIds.Remove(Preset.ActionsColumnId);

            return !_defaultColumnIds.SequenceEqual(columnIds);
        }
    }

    private void EnsureActiveViewActionsColumnWidthCorrectly()
    {
        var actionsColumn = _sheet?.ActiveView.Columns.FirstOrDefault(u => u.ColumnId == Preset.ActionsColumnId);
        if (actionsColumn is null) return;
        // if (HasCustomRowActions)
        // {
        //     actionsColumn.Width = _rowActionsContext.Width;
        // }
    }

    private async Task AddNewView()
    {
        var clonedView = _sheet!.ActiveView.DeepClone();

        clonedView.Value.Id = Guid.NewGuid();
        clonedView.Value.Name = "Copy of " + clonedView.Value.Name;
        var count = _sheet.Views.Count(u => u.Value.Name == clonedView.Value.Name);
        if (count > 0)
        {
            clonedView.Value.Name += $" ({count})";
        }

        clonedView.IsDefaultView = false;
        clonedView.AccessRole = Role;
        clonedView.State = ViewState.Unsaved;

        _sheet.Views.Add(clonedView);
        _sheet.SetActiveView(clonedView.Value.Id);
        EnsureActiveViewActionsColumnWidthCorrectly();

        ResetSheet();
        await Task.CompletedTask;
    }

    private async Task ResetView()
    {
        foreach (var column in _sheet!.ActiveView.Columns.Where(u => !Preset.InternalColumnIds.Contains(u.ColumnId)))
        {
            column.Hidden = false;
            column.Width = 0;
            column.Fixed = ColumnFixed.None;
        }

        _sheet.ActiveView.Order = [];
        _sheet.ActiveView.Value.Filter = new();
        _sheet.ActiveView.Value.Sort = new();
        _sheet.ActiveView.PageIndex = 1;
        await RefreshItemsAsync(GetItemsProviderRequest());
    }

    private void RenameView((Guid id, string Name) args)
    {
        var view = _sheet!.Views.FirstOrDefault(u => u.Value.Id == args.id);
        if (view is not null)
        {
            view.Value.Name = args.Name;
        }
    }

    private async Task DeleteView(ViewInfo view)
    {
        if (_sheet!.Views.Count < 1)
        {
            return;
        }

        // TODO: i18n
        var confirmed = await PopupService.ConfirmAsync(
            "Delete view",
            $"Are you sure you want to delete this view ({view.Value.Name})?",
            AlertTypes.Warning);

        if (!confirmed) return;

        if (view.State != ViewState.Unsaved)
        {
            try
            {
                await OnUserViewDelete.InvokeAsync(view.Value);
            }
            catch (Exception e)
            {
                await PopupService.EnqueueSnackbarAsync(e);
                return;
            }
        }

        var viewInfo = _sheet.Views.First(v => v.Value.Id == view.Value.Id);
        _sheet.Views.Remove(viewInfo);

        if (_sheet.ActiveViewId == view.Value.Id)
        {
            _sheet.SetActiveView(_sheet.Views[0].Value.Id);
        }
    }

    private async Task SaveView()
    {
        if (!OnUserViewAdd.HasDelegate)
        {
            return;
        }

        try
        {
            await OnUserViewAdd.InvokeAsync(_sheet!.ActiveView.ToView());
            _sheet.ActiveView.State = ViewState.Unmodified;
        }
        catch (Exception e)
        {
            await PopupService.EnqueueSnackbarAsync(e);
        }
    }

    private async Task HandleOnActiveViewChanged(Guid viewId)
    {
        _sheet!.SetActiveView(viewId);

        _viewActionsContext = new ViewActionsContext();

        ResetSheet();

        if (_sheet.ActiveView.Rows is null)
        {
            _sheet.ActiveView.PageIndex = 1;
            _sheet.ActiveView.PageSize = DefaultPageSize;

            await RefreshItemsAsync(GetItemsProviderRequest());
        }
        else
        {
            _rows = _sheet.ActiveView.Rows;
        }
    }

    private void ResetSheet()
    {
        // reset the width of the table
        _ = JSRuntime.InvokeVoidAsync(JsInteropConstants.SetStyle, _viewer?.GetTableSelector(), "width", null);

        UpdateStateOfActiveView();
    }

    private void HandleOnColumnOrderChanged(List<string> columnOrder)
    {
        _sheet!.ActiveView.Order = columnOrder;

        if (!_sheet.ActiveView.Order.Contains(Preset.ActionsColumnId))
        {
            _sheet.ActiveView.Order.Add(Preset.ActionsColumnId);
        }

        if (!_sheet.ActiveView.Order.Contains(Preset.RowSelectColumnId))
        {
            _sheet.ActiveView.Order.Insert(0, Preset.RowSelectColumnId);
        }
    }

    private void HandleOnRowHeightChanged(RowHeight rowHeight)
    {
        _rowHeight = rowHeight;
        _sheet!.UpdateActiveViewRowHeight(rowHeight);
    }

    private async Task HandleOnPageSizeOptionsChanged(List<int> pageSizeOptions)
    {
        _sheet!.Pagination.PageSizeOptions = pageSizeOptions;
        if (pageSizeOptions.Count == 0)
        {
            return;
        }

        pageSizeOptions.Sort();

        if (!pageSizeOptions.Contains(_sheet.ActiveView.PageSize))
        {
            _sheet.ActiveView.PageIndex = 1;
            _sheet.ActiveView.PageSize = pageSizeOptions[0];

            await RefreshItemsAsync(GetItemsProviderRequest());
        }
    }

    private void ToggleColumn(string columnId)
    {
        if (columnId == Preset.ActionsColumnId)
        {
            _sheet!.ActiveView.Value.ShowActions = !_sheet.ActiveView.Value.ShowActions;
            return;
        }

        if (columnId == Preset.RowSelectColumnId)
        {
            _sheet!.ActiveView.Value.ShowSelect = !_sheet.ActiveView.Value.ShowSelect;
            return;
        }

        var viewColumn = _sheet!.ActiveViewColumns.First(u => u.ColumnId == columnId);
        viewColumn.Hidden = !viewColumn.Hidden;
    }

    private void OpenColumnEditDialog(Column column)
    {
        var viewColumn = _sheet!.ActiveView.Columns.First(u => u.ColumnId == column.Id);
        _columnEditDialog?.Open(column, viewColumn.Fixed);
    }

    private void OpenDetailDialog(IReadOnlyDictionary<string, JsonElement> item)
    {
        List<DetailItem> detailItems = [];
        foreach (var viewColumn in _sheet!.ActiveViewColumns)
        {
            var key = item.Keys.FirstOrDefault(u => string.Equals(u, viewColumn.ColumnId, StringComparison.OrdinalIgnoreCase));
            if (key is null || _sheet.ActiveViewHiddenColumnIds.Contains(viewColumn.ColumnId))
            {
                continue;
            }

            detailItems.Add(new DetailItem(viewColumn.Column, item[key]));
        }

        _detailDialog?.Open(detailItems);
    }

    private void OpenImageViewer(IList<string> images)
    {
        _imageViewer?.Open(images);
    }

    private void HandleOnSelect((Row, bool) args)
    {
        var (item, selected) = args;

        _sheet!.ActiveView.Selection[item] = selected;

        _viewActionsContext = new ViewActionsContext(_sheet.ActiveView.Selection);

        StateHasChanged();
    }

    private void HandleOnSelectAll(bool selected)
    {
        if (_sheet!.ActiveView.Rows is null)
        {
            return;
        }

        foreach (var item in _sheet.ActiveView.Rows)
        {
            _sheet.ActiveView.Selection[item] = selected;
        }

        _viewActionsContext = new ViewActionsContext(_sheet.ActiveView.Selection);

        StateHasChanged();
    }

    private void UpdateColumn((ColumnInfo data, ColumnFixed columnFixed) arg)
    {
        var (data, columnFixed) = arg;

        var column = _sheet!.Columns.First(u => u.Id == data.Id);
        column.Name = data.Name;
        column.Type = data.Type;
        column.Config = data.Config;
        column.ConfigObject = data.ConfigObject;
        column.Searchable = data.Searchable;

        var viewColumn = _sheet.ActiveViewColumns.First(u => u.ColumnId == data.Id);
        viewColumn.Fixed = columnFixed;
    }

    private async Task SaveFilter(Filter filter)
    {
        _sheet!.ActiveView.Value.Filter = filter;
        _sheet.ActiveView.PageIndex = 1;
        await RefreshItemsAsync(GetItemsProviderRequest());
    }

    private async Task SortUpdate(Sort sort)
    {
        _sheet!.ActiveView.Value.Sort = sort;
        _sheet.ActiveView.PageIndex = 1;
        await RefreshItemsAsync(GetItemsProviderRequest());
    }

    private async Task Search()
    {
        _sheet!.ActiveView.PageIndex = 1;
        await RefreshItemsAsync(GetItemsProviderRequest());
    }

    private async Task Remove()
    {
        await OnRemove.InvokeAsync(_viewActionsContext.SelectedRows);
    }

    private void ResizeColumn((string ColumnId, double Width) args)
    {
        var viewColumn = _sheet!.ActiveViewColumns.FirstOrDefault(u => u.ColumnId == args.ColumnId);
        if (viewColumn is null)
        {
            return;
        }

        viewColumn.Width = args.Width;
    }

    private async Task SaveSheet()
    {
        var sheet = new Sheet()
        {
            QueryBody = _sheet!.QueryBody,
            CountField = _sheet.CountField,
            Columns = _sheet.Columns.Where(u => !Preset.InternalColumnIds.Contains(u.Id)).Select(Column (u) => u),
            Views = _sheet.Views.Where(u => u.AccessRole == Role.Manager).Select(View (u) => u.ToView()).ToList(),
            Pagination = _sheet.Pagination,
            ActiveViewId = _sheet.ActiveViewId,
            DefaultViewId = _sheet.DefaultViewId,
            ItemKeyName = _sheet.ItemKeyName
        };

        await OnSave.InvokeAsync(sheet);
    }

    public async Task ReloadAsync(int? page = default, int? pageSize = default)
    {
        if (page.HasValue && page > 0)
            _sheet!.ActiveView.PageIndex = page.Value;
        if (pageSize.HasValue && pageSize > 0)
            _sheet!.ActiveView.PageSize = pageSize.Value;
        await RefreshItemsAsync(GetItemsProviderRequest());
    }
}